package testGenerator;

import gui.GUIController;
import gui.testSetup.components.TestConfSetting;
import java.util.ArrayList;
import testGenerator.antlr.branchParser.BranchBuilder;
import testGenerator.jUnitCreator.JUnitTestCreator;
import testGenerator.testMethodModel.AutoJUnitMethod;
import testGenerator.testMethodModel.GenericTestPoint;
import testGenerator.testMethodModel.TestMethod;
import testGenerator.testMethodModel.TestPath;
import testGenerator.utilities.SourceReader;

/**
 * This controller is responsible for managing the individual methods,
 * test cases, and branches within the tool. Essentially any piece of
 * global data is accessible from this controller. This controller is piped to
 * nearly every class that needs access to data such as table models.
 * 
 * @author William Whitney
 */
public class Controller
{

    private ArrayList<AutoJUnitMethod> methods;
    private AutoJUnitMethod currMethod;
    private String sourceLoc;
    private String testSetup;
    private String testTearDown;
    private TestPath currentBranch;
    private String testHeader;
    private String className;
    private final GUIController guiController;

    /**
     * Default constructor.
     */
    public Controller(GUIController guiControl)
    {
        this.guiController = guiControl;
        this.reset();
    }

    /**
     * Parses the source file that has been specified to figure out all the
     * branches thought the code.
     */
    public void interpretSource()
    {
        BranchBuilder builder = new BranchBuilder(guiController, this);
        builder.interpretSource();
    }

    /**
     * Adds a method to the current controller.
     * @param method
     */
    public void addMethod(AutoJUnitMethod method)
    {
        if (method != null)
        {
            this.methods.add(method);
            currMethod = methods.get(0);
        }
    }

    /**
     * Returns the index of the current method.
     * @return int
     */
    public int getCurrMethodIndex()
    {
        return methods.indexOf(currMethod);
    }

    /**
     * Returns number of methods.
     * @return int
     */
    public int getNumMethods()
    {
        return methods.size();
    }

    /**
     * Returns the current method.
     * @return
     */
    public AutoJUnitMethod getCurrMethod()
    {
        return this.currMethod;
    }

    /**
     * Sets the current method for the controller to the next method
     * available. This method will not go past the last method even if function
     * is called over and over.
     */
    public void nextMethod()
    {
        int currIdx = getCurrMethodIndex();
        if (currIdx < getNumMethods() - 1)
        {
            this.currMethod = methods.get(currIdx + 1);
        }
    }

    /**
     * Sets the controller's current method to the last method. This function
     * will never go past the first method even if called again.
     */
    public void prevMethod()
    {
        int currIdx = getCurrMethodIndex();

        if (currIdx > 0)
        {
            this.currMethod = methods.get(currIdx - 1);
        }
    }

    /**
     * Allows the current test branch to be set.
     * @param branch
     */
    public void setCurrBranch(TestPath branch)
    {
        this.currentBranch = branch;
    }

    /**
     * Returns the current test branch.
     * @return
     */
    public TestPath getCurrTestPath()
    {
        return this.currentBranch;
    }

    /**
     * Returns the current execution path of for the current branch.
     * @return
     */
    public String getCurrExecutionPath()
    {
        return this.currentBranch.getExecutionPath();
    }

    /**
     * Clears all existing test information. The same a creating a
     * new Controller object.
     */
    public void reset()
    {
        methods = new ArrayList<AutoJUnitMethod>();
        currMethod = new AutoJUnitMethod();
        sourceLoc = "";
        testSetup = "";
        testTearDown = "";
        currentBranch = new TestPath();
        testHeader = "";
        className = "";
    }

    /**
     * Generates the JUnit test user wants to download.
     * @param isPrintStubs will print incomplete test cases if true
     */
    public String generateJUnitTest(boolean isPrintStubs)
    {
        JUnitTestCreator testCreator = new JUnitTestCreator(this, getMethods(), isPrintStubs);
        return testCreator.getTest();
    }

    /**
     * Determines if all test branches have a complete test.
     * @return
     */
    public boolean isAllBranchesTested()
    {
        for (AutoJUnitMethod method : methods)
        {
            for (TestPath branch : method.testBranches)
            {
                if (!branch.isComplete(method))
                {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Reads the source code file specified and returns its contents.
     * @return
     */
    public String getSourceCode()
    {
        SourceReader reader = new SourceReader(sourceLoc);
        return reader.getSource();
    }

    /**
     * Returns path of current source code.
     * @return 
     */
    public String getSourceLoc() {
        return this.sourceLoc;
    }
        
    /**
     * Returns information based on the configuration setting passed in.
     * @param configType configuration types
     * @return string with the current setting for the passed in type
     */
    public String getSetupInfo(TestConfSetting configType)
    {
        if (configType == TestConfSetting.SET_UP_METHOD)
        {
            return this.testSetup;
        }
        else if (configType == TestConfSetting.TEST_HEADER)
        {
            return testHeader;
        }
        else if (configType == TestConfSetting.TEAR_DOWN_METHOD)
        {
            return this.testTearDown;
        }
        else if (configType == TestConfSetting.CODE_LOCATION)
        {
            return this.sourceLoc;
        }
        return null;
    }

    /**
     * Sets a configuration parameter.
     * @param configType configuration type
     * @param value to set configuration type to
     */
    public void setTestConf(TestConfSetting configType, String value)
    {
        switch (configType)
        {
            case CODE_LOCATION:
                this.sourceLoc = value;
                break;
            case TEST_HEADER:
                this.testHeader = value;
                break;
            case SET_UP_METHOD:
                this.testSetup = value;
                break;
            case TEAR_DOWN_METHOD:
                this.testTearDown = value;
                break;
        }
    }

    /**
     * Allows the additional test constraints field to be set for the
     * current test branch.
     * @param additionalTestConstraints to be set
     */
    public void setAdditionalTestConstraints(String additionalTestConstraints)
    {
        this.currentBranch.additionalConstraints = additionalTestConstraints;
    }

    /**
     * Returns the additional test constraints for the current branch.
     * @return
     */
    public String getAdditionalTestConstraints()
    {
        return this.currentBranch.additionalConstraints;
    }

    /**
     * Sets the expected test return value for the current branch.
     * @param expectedOutput
     */
    public void setBranchExpectedReturnValue(String expectedOutput)
    {
        this.currentBranch.expectedOutput = expectedOutput;
    }
    
    public void setBranchTestMethod(TestMethod testMethod)
    {
        this.currentBranch.testMethod = testMethod;
    }

    /**
     * Returns the expected test return value for the current branch.
     * @return
     */
    public String getBranchExpectedReturnValue()
    {
        return this.currentBranch.expectedOutput;
    }

    /**
     * Return list of generic test points for branch.
     * @return
     */
    public ArrayList<GenericTestPoint> getGenericTestPoints()
    {
        return this.currentBranch.genericTestPoints;
    }

    /**
     * Get all methods available.
     * @return
     */
    public ArrayList<AutoJUnitMethod> getMethods()
    {
        return this.methods;
    }

    /**
     * Returns the class name.
     * @return
     */
    public String getClassName()
    {
        return this.className;
    }

    /**
     * Sets the class name.
     * @param className
     */
    public void setClassName(String className)
    {
        this.className = className;
    }

    /**
     * Sets the constructor to be used for the current branch.
     * @param constructor
     */
    public void setTestConstructor(String constructor)
    {
        this.currentBranch.constructor = constructor;
    }
}
